<!--
Copyright 2013 Google Inc. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Written by Steph McArthur
-->

<!DOCTYPE html><meta charset="UTF-8">
<div id="anim"></div>

<script src="../bootstrap.js"></script>
<script>
var animation = new Animation(anim, {left: "100px"}, 100);
var dt = document.timeline;

// Test AnimationPlayer.timeline.
var player1 = dt.play(animation);
test(function() {assert_equals(player1.timeline,  document.timeline)},
     "AnimationPlayer.timeline is document timeline");

// Test that TimedItem.player and AnimationPlayer.source are consistent.
test(function() {assert_equals(player1.source,  animation)},
     "AnimationPlayer.source should be the Animation");
test(function() {assert_equals(animation.player,  player1)},
     "TimedItem.player should be the AnimationPlayer");

// Test that play() always returns a new AnimationPlayer.
var player2 = dt.play(animation);
test(function() {assert_not_equals(player2,  player1)},
     "Repeated calls to play() should create a new AnimationPlayer");
test(function() {assert_equals(animation.player,  player2)},
     "Repeated play(): TimedItem.player should be the AnimationPlayer");
test(function() {assert_equals(player2.source,  animation)},
     "Repeated play(): AnimationPlayer.source should be the Animation");
test(function() {assert_equals(player2.startTime,  player1.startTime)},
     "Repeated play(): Players should have same start time");

// Test explicit setting of AnimationPlayer.source.
player1.source = animation;
test(function() {assert_equals(player2.source,  null)},
     "Set AnimationPlayer.source: Old source should be null");
test(function() {assert_equals(player1.source,  animation)},
     "Set AnimationPlayer.source: New source should be the Animation");
test(function() {assert_equals(animation.player,  player1)},
     "Set AnimationPlayer.source: TimedItem.player should be the AnimationPlayer");

// Test that TimedItem.player gives null on a detached tree.
var animation2 =
    new Animation(document.getElementById("anim"), {left: "100px"});
var animationGroup = new AnimationGroup([animation2]);
test(function() {assert_equals(animationGroup.player,  null)},
     "TimedItem.player should be null for root");
test(function() {assert_equals(animation2.player,  null)},
     "TimedItem.player should be null for leaf");

// Test that TimedItem.player remotes to the root of the tree.
var player3 = dt.play(animationGroup);
test(function() {assert_equals(animation2.player,  player3)},
     "TimedItem.player should remote");

// Test that calling play() on a TimedItem with a parent results in
// reparenting.
var player4 = dt.play(animation2);
test(function() {assert_equals(animation2.parent,  null)},
     "Animation should be reparented");
test(function() {assert_equals(animationGroup.children.length, 0)},
     "Animation parent should be updated, leaving animationGroup.children.length = 0");
test(function() {assert_equals(player4.source,  animation2)},
     "AnimationPlayer should use reparented animation");

// Test that setting a parent on a TimedItem with an AnimationPlayer causes the
// player to be disconnected.
animationGroup.append(animation2);
test(function() {assert_equals(animationGroup.player,  player3)},
     "TimedItem.player should be updated for root");
test(function() {assert_equals(animation2.player,  player3)},
     "TimedItem.player should be updated for leaf");
test(function() {assert_equals(player4.source,  null)},
     "AnimationPlayer.source should be updated");
test(function() {assert_equals(animation2.parent,  animationGroup)},
     "Animation.parent should be updated");
test(function() {assert_equals(animationGroup.children.length,  1)},
      "Animation parent should be updated, leaving animationGroup.children.length = 1");
test(function() {assert_equals(animationGroup.children[0],  animation2)},
     "Animation parent should be updated");

// Test that currentTime is zero before timeline starts.
test(function() {assert_equals(document.timeline.play(animation).currentTime,  0)},
     "currentTime should be zero before Timeline starts");

// Test that startTime is zero before timeline starts.
test(function() {assert_equals(document.timeline.play(animation).startTime,  0)},
     "startTime should be zero before Timeline starts");

// Test that setting currentTime has an effect before the timeline starts.
var player5 = dt.play(animation);
player5.currentTime = 42;
test(function() {assert_equals(player5.currentTime, 42)},
     "player5 can be seeked before timeline starts");
timing_test(function() {
    at(0.1, function() {
      assert_equals(player5.currentTime, 42.1);
    });
  }, "player5's updates to currentTime should persist after Timeline starts");

// Test that setting startTime has an effect before the timeline starts.
var player6 = dt.play(animation.clone());
player6.startTime = 42;
test(function() {assert_equals(player6.currentTime, -42)},
     "player6's currentTime should always be zero before Timeline starts");

// Test that an AnimationPlayer's TimedItem gets a null inherited time until the
// timeline starts.
var animation1 = new Animation(document.getElementById("anim"), {left: "100px"});
dt.play(animation1);
test(function() {assert_equals(animation1.localTime, null)},
     "TimedItem's inherited time, and thus " +
     "localTime should be null before Timeline starts");
timing_test(function() {
    at(0.0, function() {assert_not_equals(animation1.localTime, null)});
  }, "TimedItem's inherited time " +
     "and thus localTime should not be null after Timeline starts");

// Test that TimedItem is updated immediately on calling Timeline.play() after
// the timeline starts.
var animation4 = new Animation(
   document.getElementById("anim"), {left: "100px"});
test(function() {assert_equals(animation4.localTime,  null)},
     "localTime should be null before playing");
timing_test(function() {
    at(0.0, function() {
      document.timeline.play(animation4);
      assert_equals(animation4.localTime,  0)
    });
  }, "localTime should be set by playing");

// Test that updates to currentTime take immediate effect.
timing_test(function() {
    at(0.0, function() {
      var animation5 = new Animation(
        document.getElementById("anim"), {left: "100px"}, 100);
      document.timeline.play(animation5).currentTime = 42;
      assert_equals(animation5.localTime,  42);
    });
  }, "Updates to AnimationPlayer.currentTime should take immediate effect");

// Test that updates to source take immediate effect.
timing_test(function() {
    at(0.0, function() {
      var player = document.timeline.play(new Animation(
        document.getElementById("anim"), {left: "100px"}));
      var animation7 = new Animation(
        document.getElementById("anim"), {left: "100px"});
      player.source = animation7;
      assert_equals(animation7.localTime,  0);
    });
  }, "Updates to AnimationPlayer.source should take immediate effect");

function createPlayer(duration) {
  return document.timeline.play(new Animation(null, null, duration));
}

// Tests the initial values of the AnimationPlayer API.
timing_test(function() {
    at(0, function() {
      var anim = new Animation(null, null, 3);
      assert_equals(anim.player, null, "player should be null initially");

      // Check state after playing
      document.timeline.play(anim);
      assert_not_equals(anim.player, null, "player should be not be null when playing");
      assert_equals(anim.player.source, anim, "Initial source content");
      assert_equals(anim.player.timeline, document.timeline, "Initial document timeline");
      assert_equals(anim.player.startTime, document.timeline.currentTime, "Initial start time");
      assert_equals(anim.player.currentTime, 0, "Initial current time");
      assert_equals(anim.player.timeLag, 0, "Initial time lag");
      assert_equals(anim.player.playbackRate, 1, "Initial playback rate");
      assert_false(anim.player.paused, "Initial paused state");
      assert_false(anim.player.finished, "Initial finished state");
    });
  }, "Initial state");

// Test paused states after pausing.
test(function() {
    var player = createPlayer(3);

    // Pause
    player.pause();
    assert_true(player.paused, "Paused state after pausing");
    assert_false(player.finished, "Finished state after pausing");

    // Unpause
    player.play();
    assert_false(player.paused, "Paused state after unpausing");
    assert_false(player.finished, "Finished state after unpausing");
  }, "Pause");

// Test seeking behind startTime for a forwards player.
timing_test(function() {
    var player = createPlayer(3);

    // Seek before start
    player.currentTime = -0.1;
    assert_equals(player.currentTime, -0.1, "Current time after seeking before start");
    assert_false(player.paused, "Paused state after seeking before start");
    assert_false(player.finished, "Finished state after seeking before start");

    // Check player does progress
    at(0.2, function() {
      assert_equals(player.currentTime, 0.1, "Current time should progress");
    });
  }, "Seek behind content (forwards)");

// Test players do not get bounded after updating startTime such that they are before the start time.
timing_test(function() {
    var player = createPlayer(3);

    // Set start time in the future
    player.startTime = 0.1;

    // Check state of player
    assert_equals(player.currentTime, -0.1, "Current time while waiting to start");
    assert_false(player.paused, "Paused state while waiting to start");
    assert_false(player.finished, "Finished state while waiting to start");

    // Check player starts automatically
    at(0.2, function() {
      assert_equals(player.currentTime, 0.1, "Current time should progress");
      assert_false(player.paused, "Paused state after starting");
      assert_false(player.finished, "Finished state after starting");
    });
  }, "No bounding at start");

// Test players get bounded when they finish their source content.
timing_test(function() {
    var player = createPlayer(3);

    // Seek to just before end
    player.currentTime = 2.9;

    // Check state of player
    assert_equals(player.currentTime, 2.9, "Current time just before end");
    assert_false(player.paused, "Paused state just before end");
    assert_false(player.finished, "Finished state just before end");

    // Check player after finishing
    at(0.2, function() {
      assert_equals(player.currentTime, 3.0, "Current time when bounded progress");
      assert_false(player.paused, "Paused state after ending");
      assert_true(player.finished, "Finished state after ending");
    });
  }, "Limiting at end (seek with current time)");

// Test players get bounded when they finish their source content after altering start time.
timing_test(function() {
    var player = createPlayer(3);

    // Seek to just before end using the start time
    player.startTime -= 2.9;

    // Check state of player
    assert_equals(player.currentTime, 2.9, "Current time just before end");
    assert_false(player.paused, "Paused state just before end");
    assert_false(player.finished, "Finished state just before end");

    // Check player after finishing
    at(0.2, function() {
      assert_equals(player.currentTime, 3.0, "Current time when bounded progress");
      assert_false(player.paused, "Paused state after ending");
      assert_true(player.finished, "Finished state after ending");
    });
  }, "Limiting at end (seek with start time)");

// Test reversing a player prior to starting doesn't snap the current time to the beginning.
timing_test(function() {
    var player = createPlayer(3);

    // Seek before start
    player.currentTime = -0.1;

    // Check state
    assert_equals(player.currentTime, -0.1, "Current time before reversing");
    assert_false(player.finished, "Finished state before reversing");

    // Reverse
    player.reverse();
    assert_equals(player.currentTime, -0.1, "Current time after reversing");
    assert_true(player.finished, "Finished state after reversing");
    assert_equals(player.playbackRate, -1, "Playback rate after reversing");

    // Check player after some time
    at(0.2, function() {
      assert_equals(player.currentTime, -0.1, "Current time later");
      assert_true(player.finished, "Finished state later");
    });
  }, "reverse() before start");

// Test reversing a player at the start causes the player to become bounded.
timing_test(function() {
    var player = createPlayer(3);

    // Reverse
    player.reverse();
    assert_equals(player.currentTime, 0, "Current time after reversing");
    assert_true(player.finished, "Finished state after reversing");
    assert_equals(player.playbackRate, -1, "Playback rate after reversing");

    // Check player after some time
    at(0.2, function() {
      assert_equals(player.currentTime, 0, "Current time later");
      assert_true(player.finished, "Finished state later");
    });
  }, "reverse() at start");

// Test reversing a player halfway through an animation.
timing_test(function() {
    var player = createPlayer(3);

    // Seek midway
    player.currentTime = 1.5;

    // Reverse
    player.reverse();
    assert_equals(player.currentTime, 1.5, "Current time after reversing");
    assert_false(player.finished, "Finished state after reversing");
    assert_equals(player.playbackRate, -1, "Playback rate after reversing");

    // Check player after some time
    at(0.2, function() {
      assert_equals(player.currentTime, 1.3, "Current time when reversing");
      assert_false(player.finished, "Finished state later");
    });
  }, "reverse() while playing");

// Test reversing a player that has ended starts playing backwards.
timing_test(function() {
    var player = createPlayer(3);

    // Seek to just before the end
    player.currentTime = 2.9;

    // Wait to finish
    at(0.2, function() {
      assert_equals(player.currentTime, 3.0, "Current time when finished");
      assert_true(player.finished, "Finished state after finished");

      // Reverse
      player.reverse();
      assert_equals(player.currentTime, 3.0, "Current time after reversing");
      assert_false(player.finished, "Finished state after reversing");
      assert_equals(player.playbackRate, -1, "Playback rate after reversing");

      // Check we actually reverse
      at(0.2, function() {
        assert_equals(player.currentTime, 2.8, "Current time after beginning reverse");
        assert_false(player.finished, "Finished state after reversing");
      });
    });
  }, "reverse() when finished");

// Test reversing a player seeked past the end will snap back to the end.
timing_test(function() {
    var player = createPlayer(3);

    // Seek to past the end
    player.currentTime = 4.0

    player.reverse();
    assert_equals(player.currentTime, 3.0, "Current time after reversing");
    assert_false(player.finished, "Finished state after reversing");
    assert_equals(player.playbackRate, -1, "Playback rate after reversing");

    // Check we actually reverse
    at(0.2, function() {
      assert_equals(player.currentTime, 2.8, "Current time after beginning reverse");
      assert_false(player.finished, "Finished state after reversing");
    });
  }, "reverse() when seeked past end");

// Test player current time after adjusting start time.
test(function() {
    var player = createPlayer(3);

    player.startTime = 1;

    assert_equals(player.currentTime, -1, "currentTime while waiting to start");
    assert_false(player.finished, "Finished state while waiting to start");

    // Then put it in range
    player.startTime -= 2;
    assert_equals(player.currentTime, 1, "currentTime after updating start time");
    assert_false(player.finished, "Finished state while after updating start time");
  }, "Adjust start time (1)");

// Test player current time after adjusting start time and playing.
timing_test(function() {
    var player = createPlayer(3);

    player.startTime = 1;

    assert_equals(player.currentTime, -1, "currentTime while waiting to start");
    assert_false(player.finished, "Finished state while waiting to start");

    // Then adjust start time
    player.startTime -= 0.75;

    // Check player is still waiting
    assert_equals(player.currentTime, -0.25, "currentTime while waiting to start");
    assert_false(player.finished, "Finished state while still waiting to start");

    // Verify the time *did* update though
    at(0.5, function() {
      assert_equals(player.currentTime, 0.25, "currentTime after starting to play");
      assert_false(player.finished, "Finished state after starting to play");
    });
  }, "Adjust start time (2)");

// Test player bounding by changing start time.
test(function() {
    var player = createPlayer(3);

    // Put it past the end
    player.startTime = -4;
    assert_equals(player.currentTime, 3, "currentTime after end");
    assert_true(player.finished, "Finished state after end");
  }, "Adjust start time (3)");

// Test bounding and unbounding a player by modifying start time.
test(function() {
    var player = createPlayer(3);

    // Put player in limited stage
    player.startTime = -4;

    assert_equals(player.currentTime, 3, "currentTime state while limited");
    assert_true(player.finished, "Finished state while limited");

    // Then put it in range
    player.startTime += 2;
    assert_equals(player.currentTime, 2, "currentTime after updating start time");
    assert_false(player.finished, "Finished state while after updating start time");
  }, "Adjust start time (4)");

// Test seeking past end of player.
timing_test(function() {
    var player = createPlayer(3);
    player.currentTime = 4;
    assert_equals(player.currentTime, 4, "currentTime before waiting");
    at(0.2, function() {
      assert_equals(player.currentTime, 4, "currentTime after waiting");
    });
  }, "Seeking past player content end and waiting");

// Test seeking before start of reversed player.
timing_test(function() {
    var player = createPlayer(3);
    player.playbackRate = -1;
    player.currentTime = -1;
    assert_equals(player.currentTime, -1, "currentTime before waiting");
    at(0.2, function() {
      assert_equals(player.currentTime, -1, "currentTime after waiting");
    });
  }, "Seeking before reversed player content start and waiting");

// Test reversing the playback rate of a bounded player.
timing_test(function() {
    var player = createPlayer(3);

    // Set up player to start in 0.1s
    player.startTime = 0.1;

    // Check state
    assert_equals(player.currentTime, -0.1, "currentTime after while waiting to start");
    assert_false(player.finished, "Finished state while waiting to start");

    // Reverse playbackRate
    player.playbackRate = -1;

    // We should maintain the same current time
    assert_equals(player.currentTime, -0.1, "currentTime after updating playbackRate");
    assert_true(player.finished, "Finished state after updating playbackRate");

    // If we wait a while we should be stuck at the end
    at(0.2, function() {
      assert_equals(player.currentTime, -0.1, "currentTime after waiting");
      assert_true(player.finished, "Finished state after waiting");

      // Call play and we should start from the end
      player.play();
      assert_equals(player.currentTime, 3, "currentTime after play()");
      assert_false(player.finished, "Finished state after play()");
    });
  }, "Reversing playbackRate before start");

// Test normal current time progression of a player after being bounded and unbounded.
timing_test(function() {
    // Set up player
    var player = createPlayer(3);

    // Seek to bounded time
    player.currentTime = 4;

    // Check state
    assert_equals(player.currentTime, 4, "currentTime while bounded");
    assert_true(player.finished, "Finished state while bounded");

    // Seek to unbounded time
    player.currentTime = 2;
    assert_equals(player.currentTime, 2, "currentTime while unbounded");
    assert_false(player.finished, "Finished state while unbounded");

    // Check it actually is playing
    at(0.2, function() {
      assert_equals(player.currentTime, 2.2, "Current time after playing unbounded");
    });
  }, "Seeking from end to playing");

// Test finishing and replaying a player in reverse.
timing_test(function() {
    // Set up player
    var player = createPlayer(3);

    // Seek to middle of interval and reverse
    player.currentTime = 2;
    player.reverse();

    // Check state
    assert_equals(player.currentTime, 2, "currentTime after reversing");
    assert_false(player.finished, "Finished state after reversing");
    assert_equals(player.playbackRate, -1, "Playback rate after reversing");
    player.finish();
    assert_equals(player.currentTime, 0, "currentTime after finishing");
    assert_true(player.finished, "Finished state after finishing");

    // Then play again
    player.play();
    assert_equals(player.currentTime, 3, "currentTime after playing again");
    assert_false(player.finished, "Finished state after playing again");

    // Check if actually progresses
    at(0.2, function() {
      assert_equals(player.currentTime, 2.8, "Current time after playing for a while");
    });
  }, "play() on a finished reversed player");

// Test player.play() seeking to start when behind the source content.
timing_test(function() {
    // Set up player to start in 2s
    var player = createPlayer(3);
    player.startTime = 2;

    // Check it is waiting to start
    assert_equals(player.currentTime, -2, "currentTime while waiting");
    assert_false(player.finished, "Finished state while waiting");

    // Call play()
    player.play();
    assert_equals(player.currentTime, 0, "currentTime after calling play()");
    assert_false(player.finished, "Finished state after calling play()");

    // Check it really is playing
    at(0.5, function() {
      assert_equals(player.currentTime, 0.5, "Current time after playing for a while");
    });
  }, "play() on player waiting to start kick-starts it");

// Test seeking a player to the start of its animation.
timing_test(function() {
    // Set up player to start in 2s
    var player = createPlayer(3);
    player.startTime = 2;

    // Check it is waiting to start
    assert_equals(player.currentTime, -2, "currentTime while waiting");
    assert_false(player.finished, "Finished state while waiting");

    // Set currentTime = 0
    player.currentTime = 0;
    assert_equals(player.currentTime, 0, "currentTime after setting currentTime = 0");
    assert_false(player.finished, "Finished state after setting currentTime = 0");

    // Check it is playing
    at(0.5, function() {
      assert_equals(player.currentTime, 0.5, "currentTime after waiting a while");
      assert_false(player.finished, "Finished state after waiting a while");
    });
  }, "currentTime = 0 on player waiting to start is ok");

// Test player becoming unbounded after source content is extended.
timing_test(function() {
    var player = createPlayer(3);

    player.currentTime = 4;
    assert_equals(player.currentTime, 4, "currentTime after setting currentTime = 4");
    assert_true(player.finished, "Finished state after setting currentTime = 4");

    // Check it is NOT playing
    at(0.2, function() {
      assert_equals(player.currentTime, 4, "currentTime after waiting a while");

      // Extend source
      player.source.timing.duration = 5;
      assert_equals(player.currentTime, 4, "currentTime after extending source");
      assert_false(player.finished, "Finished state after extending source");

      // Check it IS playing
      at(0.2, function() {
        assert_equals(player.currentTime, 4.2, "currentTime after waiting a while again");
      });
    });
  }, "Extending source unblocks player (seek past end)");

// Test player ending normally and becoming unbounded after source content is extended.
timing_test(function() {
    var player = createPlayer(3);

    // Set currentTime to just before the end
    player.currentTime = 2.9;
    assert_false(player.finished, "Finished state just before ending");

    // Play to end
    at(0.2, function() {
      assert_equals(player.currentTime, 3, "currentTime after ending normally");
      assert_true(player.finished, "Finished state after ending normally");

      // Extend source
      player.source.timing.duration = 5;
      assert_equals(player.currentTime, 3.0, "currentTime after extending source");
      assert_false(player.finished, "Finished state after extending source");

      // Check it is playing
      at(0.2, function() {
        assert_equals(player.currentTime, 3.2, "currentTime after waiting a while again");
      });
    });
  }, "Extending source unblocks player (end normally)");

// Test player becomes bounded after source content is shortened.
timing_test(function() {
    var player = createPlayer(3);

    // Set currentTime to just before the end
    player.currentTime = 2;
    assert_equals(player.currentTime, 2.0, "currentTime before shortening");
    assert_false(player.finished, "Finished state before shortening");

    // Shorten source
    player.source.timing.duration = 1;
    assert_equals(player.currentTime, 2.0, "currentTime after shortening");
    assert_true(player.finished, "Finished state after shortening");

    // Check it is not playing
    at(0.2, function() {
      assert_equals(player.currentTime, 2.0, "currentTime after shortening");
    });
  }, "Shortening source blocks player");

// Test playing player becomes bounded after source content is shortened.
timing_test(function() {
    var player = createPlayer(3);

    // Set currentTime to midway
    player.currentTime = 1.5;
    assert_equals(player.currentTime, 1.5, "currentTime before shortening");
    assert_false(player.finished, "Finished state after seeking");

    // Let it play for a while
    at(0.2, function() {
      assert_equals(player.currentTime, 1.7, "currentTime before shortening");
      assert_false(player.finished, "Finished state before shortening");

      // Shorten source
      player.source.timing.duration = 1;
      assert_equals(player.currentTime, 1.7, "currentTime after shortening");
      assert_true(player.finished, "Finished state after shortening");
    });
  }, "Shortening source blocks player when playing");

// Test reversed player does not become bounded after its source content is shortened.
timing_test(function() {
    var player = createPlayer(3);

    // Set currentTime to just before the end
    player.currentTime = 2;
    player.reverse();
    assert_equals(player.currentTime, 2.0, "currentTime before shortening");
    assert_false(player.finished, "Finished state before shortening");

    // Shorten source
    player.source.timing.duration = 1;
    assert_equals(player.currentTime, 2.0, "currentTime after shortening");
    assert_false(player.finished, "Finished state after shortening");

    // Check it is playing
    at(0.2, function() {
      assert_equals(player.currentTime, 1.8, "currentTime after shortening");
    });
  }, "Shortening source does not block in reverse");

// Test player becoming unbounded after duration 0 source content is extended.
timing_test(function() {
    // Add empty animation
    var player = createPlayer(0);

    // Check we don't progress
    assert_equals(player.currentTime, 0, "currentTime before adding content");
    assert_true(player.finished, "Finished state before adding content");

    // Wait and check we don't progress
    at(0.2, function() {
      assert_equals(player.currentTime, 0, "currentTime after waiting");
      assert_true(player.finished, "Finished state after waiting");

      // "Add" content
      player.source.timing.duration = 3;

      // We should pick up from 0, not jump 0.2s in
      assert_equals(player.currentTime, 0, "currentTime after extending");
      assert_false(player.finished, "Finished state after extending");

      // Wait and check
      at(0.2, function() {
        assert_equals(player.currentTime, 0.2, "currentTime after extending and waiting");
      });
    });
  }, "Async add content");

// Test negative player playback rate with negative current time.
timing_test(function() {
    var player = createPlayer(3);

    // Set a negative time and let it play for a while
    player.currentTime = -1;
    at(0.2, function() {
      assert_equals(player.currentTime, -0.8, "currentTime after waiting");
      assert_false(player.finished, "Finished state after waiting");

      // Reverse
      player.playbackRate = -1;
      assert_equals(player.currentTime, -0.8, "currentTime after reversing");
      assert_true(player.finished, "Finished state after reversing");
    });
  }, "Setting negative playback rate in negative space");

// Test reversed player becoming bounded when start of animation is reached.
timing_test(function() {
  var player = createPlayer(3);

  // Seek to just before end
  player.reverse();
  player.currentTime = 0.1;

  // Check state of player
  assert_equals(player.currentTime, 0.1, "Current time just before end");
  assert_false(player.paused, "Paused state just before end");
  assert_false(player.finished, "Finished state just before end");

  // Check player after finishing
  at(0.2, function() {
    assert_equals(player.currentTime, 0, "Current time when limited");
    assert_false(player.paused, "Paused state after ending");
    assert_true(player.finished, "Finished state after ending");
  });
}, "Limiting at start");

// Test the floating point accuracy of seeking a player.
test(function() {
    var player = createPlayer(3);
    // Setting a high playbackRate has caused the reported currentTime to differ
    // from what was set in previous implementations of AnimationPlayer.
    player.playbackRate = 123456;
    player.currentTime = 1.618;
    assert_equals(player.currentTime, 1.618, "currentTime after seeking to fractional time");
  }, "currentTime preserves floating point accuracy after seeking");
</script>
